{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Seaborn 直方图 (histplot) 完整教程\n",
    "\n",
    "本教程详细讲解 Seaborn 中直方图的使用方法，包括基础绘图、区间设置、统计方式、核密度估计叠加以及分组展示。\n",
    "\n",
    "## 目录\n",
    "1. 基础直方图\n",
    "2. 区间控制 (bins & binwidth)\n",
    "3. 统计方式 (stat)\n",
    "4. 核密度估计叠加 (kde)\n",
    "5. 分组展示 (hue)\n",
    "6. 综合应用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import seaborn as sns\n",
    "import matplotlib.pyplot as plt\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "\n",
    "# 设置样式\n",
    "sns.set_theme(style=\"whitegrid\")\n",
    "plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']\n",
    "plt.rcParams['axes.unicode_minus'] = False\n",
    "\n",
    "# 加载示例数据\n",
    "penguins = sns.load_dataset(\"penguins\")\n",
    "tips = sns.load_dataset(\"tips\")\n",
    "\n",
    "print(\"企鹅数据集预览：\")\n",
    "penguins.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 1. 基础直方图\n",
    "\n",
    "### 1.1 创建基础直方图\n",
    "\n",
    "使用 `sns.histplot(data, x)` 创建直方图，展示单变量的分布情况。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure(figsize=(10, 6))\n",
    "sns.histplot(data=penguins, x=\"flipper_length_mm\")\n",
    "plt.title(\"企鹅鳍长度分布\", fontsize=14)\n",
    "plt.xlabel(\"鳍长度 (mm)\")\n",
    "plt.ylabel(\"频数\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.2 直方图的作用\n",
    "\n",
    "- 观察数据的**分布形态**（正态、偏态、双峰等）\n",
    "- 识别**异常值**和**离群点**\n",
    "- 了解数据的**集中趋势**和**离散程度**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 2. 区间控制\n",
    "\n",
    "### 2.1 bins - 设置区间数量\n",
    "\n",
    "`bins` 参数控制直方图的柱子数量，影响分布的细节展示。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig, axes = plt.subplots(2, 2, figsize=(14, 10))\n",
    "\n",
    "bin_values = [5, 10, 20, 50]\n",
    "for ax, bins in zip(axes.flat, bin_values):\n",
    "    sns.histplot(data=penguins, x=\"flipper_length_mm\", bins=bins, ax=ax)\n",
    "    ax.set_title(f\"bins={bins}\")\n",
    "    ax.set_xlabel(\"鳍长度 (mm)\")\n",
    "    ax.set_ylabel(\"频数\")\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()\n",
    "\n",
    "print(\"提示：bins 太少会丢失细节，太多会产生噪声\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.2 bins - 自定义区间边界\n",
    "\n",
    "可以传递列表或数组来精确指定区间边界。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 自定义区间：[170, 180), [180, 190), ..., [220, 230)\n",
    "custom_bins = [170, 180, 190, 200, 210, 220, 230]\n",
    "\n",
    "plt.figure(figsize=(10, 6))\n",
    "sns.histplot(data=penguins, x=\"flipper_length_mm\", bins=custom_bins)\n",
    "plt.title(\"自定义区间边界\", fontsize=14)\n",
    "plt.xlabel(\"鳍长度 (mm)\")\n",
    "plt.xticks(custom_bins)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.3 binwidth - 设置区间宽度\n",
    "\n",
    "使用 `binwidth` 直接指定每个柱子的宽度。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig, axes = plt.subplots(1, 3, figsize=(16, 5))\n",
    "\n",
    "binwidths = [2, 5, 10]\n",
    "for ax, bw in zip(axes, binwidths):\n",
    "    sns.histplot(data=penguins, x=\"flipper_length_mm\", binwidth=bw, ax=ax)\n",
    "    ax.set_title(f\"binwidth={bw} mm\")\n",
    "    ax.set_xlabel(\"鳍长度 (mm)\")\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 3. 统计方式 (stat)\n",
    "\n",
    "`stat` 参数控制 y 轴的统计方式。\n",
    "\n",
    "- `'count'`（默认）：频数\n",
    "- `'frequency'`：频率（频数/总数）\n",
    "- `'density'`：密度（面积总和为1）\n",
    "- `'probability'`：概率（各柱高度和为1）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig, axes = plt.subplots(2, 2, figsize=(14, 10))\n",
    "\n",
    "stats = ['count', 'frequency', 'density', 'probability']\n",
    "for ax, stat in zip(axes.flat, stats):\n",
    "    sns.histplot(data=penguins, x=\"flipper_length_mm\", stat=stat, ax=ax)\n",
    "    ax.set_title(f\"stat='{stat}'\")\n",
    "    ax.set_xlabel(\"鳍长度 (mm)\")\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.1 各统计方式的应用场景\n",
    "\n",
    "- **count**：查看绝对数量\n",
    "- **frequency**：比较不同样本量的数据\n",
    "- **density**：与核密度估计曲线对比\n",
    "- **probability**：概率分析"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 4. 核密度估计叠加 (kde)\n",
    "\n",
    "### 4.1 添加 KDE 曲线\n",
    "\n",
    "设置 `kde=True` 在直方图上叠加核密度估计曲线，展示平滑的分布趋势。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig, axes = plt.subplots(1, 2, figsize=(14, 5))\n",
    "\n",
    "# 不带 KDE\n",
    "sns.histplot(data=penguins, x=\"flipper_length_mm\", ax=axes[0])\n",
    "axes[0].set_title(\"不带 KDE 曲线\")\n",
    "\n",
    "# 带 KDE\n",
    "sns.histplot(data=penguins, x=\"flipper_length_mm\", kde=True, ax=axes[1])\n",
    "axes[1].set_title(\"叠加 KDE 曲线\")\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.2 KDE 与 stat='density' 配合\n",
    "\n",
    "使用 `stat='density'` 时，直方图和 KDE 曲线的纵轴单位一致。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure(figsize=(10, 6))\n",
    "sns.histplot(data=penguins, x=\"flipper_length_mm\", \n",
    "             stat='density', kde=True, bins=20)\n",
    "plt.title(\"密度直方图 + KDE 曲线\", fontsize=14)\n",
    "plt.xlabel(\"鳍长度 (mm)\")\n",
    "plt.ylabel(\"密度\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 5. 分组展示 (hue)\n",
    "\n",
    "### 5.1 使用 hue 按类别分组\n",
    "\n",
    "通过 `hue` 参数在同一图中展示不同类别的分布。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure(figsize=(10, 6))\n",
    "sns.histplot(data=penguins, x=\"flipper_length_mm\", hue=\"species\")\n",
    "plt.title(\"不同企鹅物种的鳍长度分布\", fontsize=14)\n",
    "plt.xlabel(\"鳍长度 (mm)\")\n",
    "plt.ylabel(\"频数\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.2 multiple 参数 - 控制重叠方式\n",
    "\n",
    "- `'layer'`（默认）：层叠显示\n",
    "- `'dodge'`：并排显示\n",
    "- `'stack'`：堆叠显示\n",
    "- `'fill'`：填充为100%"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig, axes = plt.subplots(2, 2, figsize=(14, 10))\n",
    "\n",
    "multiples = ['layer', 'dodge', 'stack', 'fill']\n",
    "for ax, mult in zip(axes.flat, multiples):\n",
    "    sns.histplot(data=penguins, x=\"flipper_length_mm\", \n",
    "                 hue=\"species\", multiple=mult, ax=ax)\n",
    "    ax.set_title(f\"multiple='{mult}'\")\n",
    "    ax.set_xlabel(\"鳍长度 (mm)\")\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.3 分组 + KDE"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure(figsize=(12, 6))\n",
    "sns.histplot(data=penguins, x=\"flipper_length_mm\", \n",
    "             hue=\"species\", kde=True, stat='density', \n",
    "             alpha=0.5, bins=20)\n",
    "plt.title(\"分组密度直方图 + KDE\", fontsize=14)\n",
    "plt.xlabel(\"鳍长度 (mm)\")\n",
    "plt.ylabel(\"密度\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "## 6. 综合应用\n",
    "\n",
    "### 6.1 完整示例：餐厅小费分析"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig, axes = plt.subplots(2, 2, figsize=(14, 10))\n",
    "\n",
    "# 1. 基础分布\n",
    "sns.histplot(data=tips, x=\"total_bill\", bins=30, ax=axes[0, 0])\n",
    "axes[0, 0].set_title(\"账单总额分布\")\n",
    "\n",
    "# 2. 密度 + KDE\n",
    "sns.histplot(data=tips, x=\"total_bill\", stat='density', \n",
    "             kde=True, bins=30, ax=axes[0, 1])\n",
    "axes[0, 1].set_title(\"密度分布 + KDE\")\n",
    "\n",
    "# 3. 按时间分组\n",
    "sns.histplot(data=tips, x=\"total_bill\", hue=\"time\", \n",
    "             multiple='dodge', bins=20, ax=axes[1, 0])\n",
    "axes[1, 0].set_title(\"午餐 vs 晚餐\")\n",
    "\n",
    "# 4. 按性别分组（堆叠）\n",
    "sns.histplot(data=tips, x=\"total_bill\", hue=\"sex\", \n",
    "             multiple='stack', bins=20, ax=axes[1, 1])\n",
    "axes[1, 1].set_title(\"按性别堆叠\")\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.2 高级示例：多维度分析"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建子图网格\n",
    "g = sns.FacetGrid(penguins, col=\"species\", row=\"sex\", height=3.5, aspect=1.2)\n",
    "g.map(sns.histplot, \"flipper_length_mm\", bins=15, kde=True, stat='density')\n",
    "g.set_axis_labels(\"鳍长度 (mm)\", \"密度\")\n",
    "g.set_titles(col_template=\"{col_name}\", row_template=\"{row_name}\")\n",
    "g.fig.suptitle(\"不同物种和性别的鳍长度分布\", y=1.02, fontsize=16, fontweight='bold')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 总结\n",
    "\n",
    "### 直方图参数选择指南\n",
    "\n",
    "1. **bins/binwidth**：\n",
    "   - 数据量小（<100）：bins=10-20\n",
    "   - 数据量大（>1000）：bins=30-50\n",
    "   - 使用 Sturges 法则：bins ≈ log₂(n) + 1\n",
    "\n",
    "2. **stat**：\n",
    "   - 单一数据集：使用 `'count'`\n",
    "   - 比较不同样本量：使用 `'density'` 或 `'probability'`\n",
    "   - 配合 KDE：使用 `'density'`\n",
    "\n",
    "3. **kde**：\n",
    "   - 数据量充足（>50）且分布连续：建议添加\n",
    "   - 离散数据或数据量少：不建议\n",
    "\n",
    "4. **hue + multiple**：\n",
    "   - 2-3个类别：`'layer'` 或 `'dodge'`\n",
    "   - 查看占比：`'fill'`\n",
    "   - 查看总量：`'stack'`\n",
    "\n",
    "### 最佳实践\n",
    "\n",
    "- 先用默认参数快速查看\n",
    "- 调整 bins 找到最佳细节展示\n",
    "- 添加 KDE 观察平滑趋势\n",
    "- 使用 hue 进行分组对比\n",
    "- 结合 FacetGrid 进行多维度分析"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
